﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;

using Microsoft.Samples.Office.UnifiedCommunications.Gadget.Config;

namespace Microsoft.Samples.Office.UnifiedCommunications.Gadget.EWSUtility
{
    public class EWSRequest
    {
        public string url { get; set; }
        public string userName { get; set; }
        public string password { get; set; }
        public string domain { get; set; }
        public string EWSRequestString { get; set; }

        private string basedate = "{0}-{1}-{2}T12:00:00Z";

        public EWSRequest (string url, string userName, string password, string domain)
        {
            this.url = url;
            this.userName = userName;
            this.password = password;
            this.domain = domain;
        }

        public EWSRequest (string url, string userName, string password, string domain, string EWSRequestString)
        {
            this.url = url;
            this.userName = userName;
            this.password = password;
            this.domain = domain;
            this.EWSRequestString = EWSRequestString;
        }

        public XmlDocument MakeRawSoapRequest(params string[] headers)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "text/xml;utf-8";
            request.Credentials = new NetworkCredential(userName, password, domain);

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            sb.AppendLine("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
            sb.AppendLine("xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"");
            sb.AppendLine("xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"");
            sb.AppendLine("xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\"");
            sb.AppendLine("xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">");

            // Add soap headers
            //
            if ((headers != null && headers.Length > 0))
            {
                sb.AppendLine("<soap:Header>");
                foreach (string header in headers)
                {
                    sb.Append(header);
                }
                sb.AppendLine("</soap:Header>");
            }

            sb.AppendLine("<soap:Body>");
            sb.Append(EWSRequestString); // Append the soap request
            sb.AppendLine("</soap:Body>");
            sb.AppendLine("</soap:Envelope>");

            // Grab the request as a byte array
            //
            byte[] requestBytes = Encoding.UTF8.GetBytes(sb.ToString());
            request.ContentLength = requestBytes.Length;

            // Write request bytes to the web request
            //
            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(requestBytes, 0, requestBytes.Length);
                requestStream.Flush();
                requestStream.Close();
            }

            HttpWebResponse response;
            try
            {
                response = request.GetResponse() as HttpWebResponse; // Try to get the response
            }
            catch (WebException webException)
            {
                HttpWebResponse exceptionResponse = webException.Response as HttpWebResponse;

                if (exceptionResponse.StatusCode == HttpStatusCode.Unauthorized || exceptionResponse.StatusCode == HttpStatusCode.Forbidden)
                {
                    throw (new Exception(String.Concat(ConfigurationManager.AppSettings["EWSUnauthorizedException"], ": ", exceptionResponse.StatusCode.ToString())));
                }
                else
                {
                    using (Stream exceptionResponseStream = exceptionResponse.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(exceptionResponseStream))
                        {
                            throw new Exception(reader.ReadToEnd());
                        }
                    }
                }
            }

            // Read the response stream
            //
            string responseString;
            using (Stream responseStream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(responseStream))
                {
                    responseString = reader.ReadToEnd();
                }                
            }

            // Load response into XmlDocument
            //
            XmlDocument doc = new XmlDocument();
            int xmlStartIndex = responseString.IndexOf("<?xml");
            doc.LoadXml(responseString.Substring(xmlStartIndex));
            return doc;
        }

        public XmlDocument GetFolderByDistinguishedFolderId(EWSEnums.DistinguishedFolderId distinguishedFolderId)
        {
            try
            {
                // Format Soap request
                //
                StringBuilder request = new StringBuilder();
                request.AppendLine("<GetFolder>");
                request.AppendLine("    <FolderShape>");
                request.AppendLine("        <t:BaseShape>AllProperties</t:BaseShape>");
                request.AppendLine("    </FolderShape>");
                request.AppendLine("    <FolderIds>");
                request.AppendFormat("        <t:DistinguishedFolderId Id=\"{0}\"/>", distinguishedFolderId.ToString());
                request.AppendLine("    </FolderIds>");
                request.AppendLine("</GetFolder>");
                this.EWSRequestString = request.ToString();

                return MakeRawSoapRequest();
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        public XmlDocument GetFolderItems(EWSEnums.DistinguishedFolderId[] folders, EWSEnums.BaseShape baseShape,
            EWSEnums.SortOrder sortOrder, string sortBy, int? limit)
        {
            try
            {
                // Format the request
                //
                StringBuilder request = new StringBuilder();

                request.AppendLine("<FindItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\"");
                request.AppendLine("  xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\"");
                request.AppendLine("  Traversal=\"Shallow\">");
                request.AppendLine("    <ItemShape>");
                request.AppendFormat("        <t:BaseShape>{0}</t:BaseShape>", baseShape);
                request.AppendLine("    </ItemShape>");

                if (!string.IsNullOrEmpty(sortBy))
                {
                    request.AppendLine("<SortOrder>");
                    request.AppendFormat("  <t:FieldOrder Order=\"{0}\">", sortOrder.ToString());
                    request.AppendFormat("      <t:FieldURI FieldURI=\"{0}\"/>", sortBy);
                    request.AppendLine("    </t:FieldOrder>");
                    request.AppendLine("</SortOrder>");
                }

                request.AppendLine("    <ParentFolderIds>");
                for (int i = 0; i <= folders.GetUpperBound(0); i++)
                    request.AppendFormat("        <t:DistinguishedFolderId Id=\"{0}\"/>", folders[i]);
                request.AppendLine("    </ParentFolderIds>");

                request.AppendLine("</FindItem>");

                this.EWSRequestString = request.ToString();

                return MakeRawSoapRequest();
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        public XmlDocument GetFolderCountTodayByContact(EWSEnums.DistinguishedFolderId[] folders, EWSEnums.BaseShape baseShape, string groupBy)
        {
            string curDate = String.Format(basedate, DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString(), DateTime.Now.Day.ToString());
            // Format the request
            //
            StringBuilder request = new StringBuilder();

            request.AppendLine("<FindItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\"");
            request.AppendLine("  xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\"");
            request.AppendLine("  Traversal=\"Shallow\">");
            request.AppendLine("    <ItemShape>");
            request.AppendFormat("        <t:BaseShape>{0}</t:BaseShape>", baseShape);
            request.AppendLine("    </ItemShape>");

            //if (!string.IsNullOrEmpty(groupBy))
            //{
            //    request.AppendLine("<GroupBy>");
            //    request.AppendFormat("      <t:FieldURI FieldURI=\"message:{0}\"/>", groupBy);
            //    request.AppendLine("</GroupBy>");
            //}

            request.AppendLine("<Restriction>");
            request.AppendLine("    <t:IsGreaterThan>");
            request.AppendLine("        <t:FieldURI FieldURI=\"item:DateTimeReceived\" />");
            request.AppendLine("        <t:FieldURIOrConstant>");
            request.AppendLine("             <t:Constant Value=\"");
            request.AppendLine(curDate);
            request.AppendLine("\" />");
            request.AppendLine("        </t:FieldURIOrConstant>");
            request.AppendLine("    </t:IsGreaterThan>");
            request.AppendLine("</Restriction>");

            request.AppendLine("    <ParentFolderIds>");
            for (int i = 0; i <= folders.GetUpperBound(0); i++)
                request.AppendFormat("        <t:DistinguishedFolderId Id=\"{0}\"/>", folders[i]);
            request.AppendLine("    </ParentFolderIds>");

            request.AppendLine("</FindItem>");

            this.EWSRequestString = request.ToString();

            return MakeRawSoapRequest();
        }

        public static XmlNamespaceManager CreateRequestXmlNamespaceManager(XmlDocument xmlDocument)
        {
            XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);

            xmlNamespaceManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
            xmlNamespaceManager.AddNamespace("m", "http://schemas.microsoft.com/exchange/services/2006/messages");
            xmlNamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
            xmlNamespaceManager.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
            xmlNamespaceManager.AddNamespace("t", "http://schemas.microsoft.com/exchange/services/2006/types");

            return xmlNamespaceManager;
        }

        public static string PrefixXPath(string query)
        {
            return string.Concat("/soap:Envelope/soap:Body/", query);
        }
    }
}